package org.iabako.server.dao.report;

import org.iabako.shared.dto.FinancesBalance;
import org.iabako.shared.dto.ReportGenerationInputDTO;
import org.springframework.stereotype.Repository;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by jose on 26/01/15.
 */
@Repository("incomeReportDAO")
public class IncomeReportDAO extends AbstractReportDAO {

    public Map<Object, FinancesBalance> getSalesBy(ReportGenerationInputDTO reportGenerationInputDTO, Date dateAfter, Date dateBefore) {

        Map<Object, FinancesBalance> salesBy = new HashMap<Object, FinancesBalance>();

        addPaymentsByDate(reportGenerationInputDTO, salesBy, dateAfter, dateBefore);

        addPaymentsByScheduledDate(reportGenerationInputDTO, salesBy, dateAfter, dateBefore);

        addInstallmentsByScheduledDate(reportGenerationInputDTO, salesBy, dateAfter, dateBefore);

        return salesBy;
    }

    private void addInstallmentsByScheduledDate(ReportGenerationInputDTO reportGenerationInputDTO,
                                                Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {

        switch (reportGenerationInputDTO.getReportByEnum()) {
            case byClient:
                addInstallmentsByScheduledDateByClient(salesBy, dateAfter, dateBefore);
                break;
            case bySale:
                addInstallmentsByScheduledDateBySale(salesBy, dateAfter, dateBefore);
                break;
        }
    }

    private void addInstallmentsByScheduledDateBySale(Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        String query = "SELECT sale, SUM(i.amount) FROM Installment i join i.sale sale " +
                "WHERE i.date >= :dateAfter AND i.date < :dateBefore AND sale.enterprise IN (:enterprises) " +
                "GROUP BY sale";

        List<Object[]> result = (List<Object[]>) entityManager.createQuery(query).
                setParameter("dateAfter", dateAfter).setParameter("dateBefore", dateBefore).
                setParameter("enterprises", getSessionUser().getEnterprise().getVisibleEnterprisesFinancialData()).getResultList();

        addIncomeToMapByEntity(salesBy, result, false);
    }

    private void addInstallmentsByScheduledDateByClient(Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        String query = "SELECT sale.client, SUM(i.amount) FROM Installment i join i.sale sale " +
                "WHERE i.date >= :dateAfter AND i.date < :dateBefore AND sale.enterprise IN (:enterprises) " +
                "GROUP BY sale.client";

        List<Object[]> result = (List<Object[]>) entityManager.createQuery(query).
                setParameter("dateAfter", dateAfter).setParameter("dateBefore", dateBefore).
                setParameter("enterprises", getSessionUser().getEnterprise().getVisibleEnterprisesFinancialData()).getResultList();

        addIncomeToMapByEntity(salesBy, result, false);
    }

    private void addPaymentsByScheduledDate(ReportGenerationInputDTO reportGenerationInputDTO,
                                            Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        switch (reportGenerationInputDTO.getReportByEnum()) {
            case byClient:
                addPaymentsByScheduledDateByClient(salesBy, dateAfter, dateBefore);
                break;
            case bySale:
                addPaymentsByScheduledDateBySale(salesBy, dateAfter, dateBefore);
                break;
        }
    }

    private void addPaymentsByScheduledDateBySale(Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        String query = "SELECT sale, SUM(p.amount) FROM Payment p join p.sale sale " +
                "WHERE p.scheduleDate >= :dateAfter AND p.scheduleDate < :dateBefore AND sale.enterprise IN (:enterprises) " +
                "GROUP BY sale";

        List<Object[]> result = (List<Object[]>) entityManager.createQuery(query).
                setParameter("dateAfter", dateAfter).setParameter("dateBefore", dateBefore).
                setParameter("enterprises", getSessionUser().getEnterprise().getVisibleEnterprisesFinancialData()).getResultList();

        addIncomeToMapByEntity(salesBy, result, false);
    }

    private void addPaymentsByScheduledDateByClient(Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        String query = "SELECT sale.client, SUM(p.amount) FROM Payment p join p.sale sale " +
                "WHERE p.scheduleDate >= :dateAfter AND p.scheduleDate < :dateBefore AND sale.enterprise IN (:enterprises) " +
                "GROUP BY sale.client";

        List<Object[]> result = (List<Object[]>) entityManager.createQuery(query).
                setParameter("dateAfter", dateAfter).setParameter("dateBefore", dateBefore).
                setParameter("enterprises", getSessionUser().getEnterprise().getVisibleEnterprisesFinancialData()).getResultList();

        addIncomeToMapByEntity(salesBy, result, false);
    }

    private void addPaymentsByDate(ReportGenerationInputDTO reportGenerationInputDTO,
                                   Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        switch (reportGenerationInputDTO.getReportByEnum()) {
            case byClient:
                addPaymentsByDateByClient(salesBy, dateAfter, dateBefore);
                break;
            case bySale:
                addPaymentsByDateBySale(salesBy, dateAfter, dateBefore);
                break;
            case byInvoice:
                addPaymentsByDateByInvoice(salesBy, dateAfter, dateBefore);
                break;
        }
    }

    private void addPaymentsByDateByInvoice(Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        String query = "SELECT p, p.amount FROM Payment p join p.sale sale " +
                "WHERE p.date >= :dateAfter AND p.date < :dateBefore AND sale.enterprise IN (:enterprises)";

        List<Object[]> result = (List<Object[]>) entityManager.createQuery(query).
                setParameter("dateAfter", dateAfter).setParameter("dateBefore", dateBefore).
                setParameter("enterprises", getSessionUser().getEnterprise().getVisibleEnterprisesFinancialData()).getResultList();

        addIncomeToMapByEntity(salesBy, result, true);
    }

    private void addPaymentsByDateBySale(Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        String query = "SELECT sale, SUM(p.amount) FROM Payment p join p.sale sale " +
                "WHERE p.date >= :dateAfter AND p.date < :dateBefore AND sale.enterprise IN (:enterprises) " +
                "GROUP BY sale";

        List<Object[]> result = (List<Object[]>) entityManager.createQuery(query).
                setParameter("dateAfter", dateAfter).setParameter("dateBefore", dateBefore).
                setParameter("enterprises", getSessionUser().getEnterprise().getVisibleEnterprisesFinancialData()).getResultList();

        addIncomeToMapByEntity(salesBy, result, true);
    }

    private void addPaymentsByDateByClient(Map<Object, FinancesBalance> salesBy, Date dateAfter, Date dateBefore) {
        String query = "SELECT sale.client, SUM(p.amount) FROM Payment p join p.sale sale " +
                "WHERE p.date >= :dateAfter AND p.date < :dateBefore AND sale.enterprise IN (:enterprises) " +
                "GROUP BY sale.client";

        List<Object[]> result = (List<Object[]>) entityManager.createQuery(query).
                setParameter("dateAfter", dateAfter).setParameter("dateBefore", dateBefore).
                setParameter("enterprises", getSessionUser().getEnterprise().getVisibleEnterprisesFinancialData()).getResultList();

        addIncomeToMapByEntity(salesBy, result, true);
    }
}
